#pragma once
// 建立正排索引（通过文档id找到内容）与倒排索引（通过内容找到文档id）

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <unordered_map>
#include <mutex>
#include "gongjv.hpp"
using namespace std;

namespace ns_index
{
    struct DocInfo
    {
        string title;
        string content;
        string url;
        uint64_t doc_id;
    };

    struct InvertedElem
    {
        uint64_t doc_id;
        string word;
        int weight;
        InvertedElem() : weight(0) {}
    };
    // 倒排拉链，存储根据内容搜索得到的对应一个或多个文档的信息
    typedef vector<InvertedElem> InvertedList;
    class Index
    {
    private:
        // 正排索引，用数组实现，数组下标作为文档id
        vector<DocInfo> forward_index;
        // 倒排索引一定是一个关键字和一组(个)InvertedElem对应[关键字和倒排拉链的映射关系]
        unordered_map<string, InvertedList> inverted_index;

    private:
        // 采用单例模式
        Index() {} // 一定要有结构体，不能delete
        Index(const Index &) = delete;
        Index &operator=(const Index &) =delete;

        static Index *instance;
        static mutex mtx;
    public:
       ~Index(){}

    public:
    //类外获取对象的接口
      static Index* GetInstance()
      {
        if(instance==nullptr)
        {
            mtx.lock();
            if(instance==nullptr)
            {
                instance=new Index();
            }
            mtx.unlock();
        }
        return instance;
      }
        // 根据文档id（doc_id）找到文档内容
        DocInfo *GetFordwardIndex(uint64_t doc_id)
        {
            if (doc_id >= forward_index.size())
            {
                cerr << "doc_id out range,error!" << endl;
                return nullptr;
            }
            return &forward_index[doc_id];
        }
        // 根据文档内容（关键字string），获得倒排拉链（存储文档内容有关键字的文档的的数组）
        InvertedList *GetInvertedList(const string &word)
        {
            auto iter = inverted_index.find(word);
            if (iter == inverted_index.end())
            {
                cerr << word << "have no InvertedList" << endl;
               
            }
             return &(iter->second);
        }
        // 把parser处理后数据raw.txt，构建正排和倒排索引
        // data/raw_html/raw.txt
        bool BuildIndex(const string &input)
        {
            ifstream in(input, ios::in | ios::binary); // 读数据
            string line;
            // 按行读，我们前面已经将每个文档的数据按行分割并存储在raw.txt中
            int count=0;
            while (getline(in, line))
            {
                DocInfo *doc = BulidForwardIndex(line);
                if (doc == nullptr)
                {
                    cerr << "bulid" << line << "error" << endl;
                    continue;
                }
                BuildInvertedIndex(*doc);
                count++;
                if(count%50==0)
                {
                    cout<<"当前已经建立索引的文档:"<<count<<endl;
                }
            }
            return true;
        }

    private:
        // 建立正排索引
        DocInfo *BulidForwardIndex(const string &line)
        {
            // 1.将line切割为标题，内容，url
            vector<string> results;
            const string sep = "\3";
            ns_gongjv::StringSplit::Split(line, &results, sep);
            if (results.size() != 3)
            {
                return nullptr;
            }
            // 2.将分割好的字符串填充到DocInfo中
            DocInfo doc;
            doc.title = results[0];
            doc.content = results[1];
            doc.url = results[2];
            doc.doc_id = forward_index.size();
            // 3.将doc保存到正排索引vector中
            forward_index.push_back(move(doc));
            return &forward_index.back();
        }
        // 建立倒排索引
        bool BuildInvertedIndex(const DocInfo &doc)
        {
            struct word_cnt
            {
                int title_cnt;
                int content_cnt;
                word_cnt() : title_cnt(0), content_cnt(0) {}
            };
            unordered_map<string, word_cnt> word_map; // 存储词频的映射表

            // 对标题进行分词
            vector<string> title_words;
            ns_gongjv::Jiebagongjv::CutString(doc.title, &title_words);
            // 对标题进行词频统计
            for (string s : title_words)
            {
                boost::to_lower(s); // 统一转化为小写
                word_map[s].title_cnt++;
            }
            // 对内容进行分词
            vector<string> centent_words;
            ns_gongjv::Jiebagongjv::CutString(doc.content, &centent_words);
            // 对内容进行词频统计
            for (string s : centent_words)
            {
                boost::to_lower(s); // 统一转化为小写
                word_map[s].content_cnt++;
            }
#define X 10
#define Y 1
            for (auto &word_pair : word_map)
            {
                InvertedElem item;
                item.doc_id = doc.doc_id;
                item.word = word_pair.first;
                item.weight = X * word_pair.second.title_cnt + Y * word_pair.second.content_cnt; // 人工设置权重
                inverted_index[word_pair.first].push_back(move(item));                           // 根据关键字找到文档id等信息
            }
            return true;
        }
    };
    Index* Index::instance = nullptr;
    std::mutex Index::mtx;
}
